package com.tang.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tang.config.JwtAuthorizationConfig;
import com.tang.core.UserDetail;
import com.tang.domian.Token;
import com.tang.utils.Base64SerializationUtils;
import com.tang.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.SneakyThrows;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Tang
 * @classname LoginFilter
 * @description [  ]
 * @date 2021/5/8 23:03
 */
@Component
public class LoginFilter implements GlobalFilter, SmartInitializingSingleton {

    private static final String AUTHORIZATION = "Authorization";

    private static final String BEARER = "Bearer";

    @Resource
    private JwtAuthorizationConfig jwtAuthorizationConfig;

    private ServerWebExchangeMatcher matcher;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Mono<Integer> next = Mono.just(1);
        return matcher.matches(exchange)
                .map(ServerWebExchangeMatcher.MatchResult::isMatch)
                .filter(isMatch -> isMatch)
                .flatMap(isMatch -> chain.filter(exchange).then(next))
                .switchIfEmpty(Mono.defer(() -> doAuth(exchange, chain).then(next)))
                .thenEmpty(Mono.empty());
    }

    @SneakyThrows
    private Mono<Void> doAuth(ServerWebExchange exchange, GatewayFilterChain chain){
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String tokenStr = request.getHeaders().getFirst(AUTHORIZATION);
        final boolean hasToken = !StringUtils.isEmpty(tokenStr) && tokenStr.startsWith(BEARER);
        if (!hasToken) {
            response.writeWith(responseData(response));
        }
        assert tokenStr != null;
        String realToken = tokenStr.replace(BEARER + " ", "");
        Token token;
        // 构造 Authentication
        if (JwtUtil.isValid(jwtAuthorizationConfig,realToken)) {
            token = JwtUtil.getToken(jwtAuthorizationConfig, realToken);
            // 适配用户信息
            UserDetail userDetail = new UserDetail(token);
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(userDetail,null,userDetail.getAuthorities());
            request.getHeaders().add("gateway-token", Base64SerializationUtils.serialize(authenticationToken));
            request.getHeaders().add("Authorization", realToken);
            exchange.mutate().request(request).build();
            return chain.filter(exchange);
        }
        return Mono.empty();
    }

    private Publisher<? extends DataBuffer> responseData(ServerHttpResponse exchangeResponse) {
        Map<String, String> responseData = new HashMap<>(8);
        responseData.put("code", "401");
        responseData.put("message", "非法请求");
        responseData.put("case", "token is empty");
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = null;
        try {
            //   转化为json输出
            bytes = objectMapper.writeValueAsBytes(responseData);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 返回给浏览器
        HttpHeaders httpHeaders = exchangeResponse.getHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        exchangeResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
        DataBufferFactory bufferFactory = exchangeResponse.bufferFactory();
        DataBuffer dataBuffer = bufferFactory.wrap(bytes);
        return Mono.just(dataBuffer);
    }

    @Override
    public void afterSingletonsInstantiated() {
        List<String> collectList = Stream.concat(
                jwtAuthorizationConfig.getPrefix().stream().map(item -> item + "/**"),
                jwtAuthorizationConfig.getSuffix().stream().map(item -> item + "/**"))
                .collect(Collectors.toList());
        matcher = ServerWebExchangeMatchers.pathMatchers(collectList.toArray(new String[]{}));
    }
}